//===--- Interval.swift.gyb ----------------------------------*- swift -*--===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// An interval over a `Comparable` type.
public protocol IntervalType {
  /// The type of the `Interval`'s endpoints.
  typealias Bound : Comparable

  /// Returns `true` iff the interval contains `value`.
  @warn_unused_result
  func contains(value: Bound) -> Bool

  /// Return `rhs` clamped to `self`.  The bounds of the result, even
  /// if it is empty, are always within the bounds of `self`.
  @warn_unused_result
  func clamp(intervalToClamp: Self) -> Self

  /// `true` iff `self` is empty.
  var isEmpty: Bool {get}


  /// The `Interval`'s lower bound.
  ///
  /// Invariant: `start` <= `end`.
  var start: Bound {get}
  /// The `Interval`'s upper bound.
  ///
  /// Invariant: `start` <= `end`.
  var end: Bound {get}
}

% for Kind, rangeOperator, upperBoundCompare in [
%   ('HalfOpen', '..<', '<'),
%   ('Closed', '...', '<=')
% ]:
%   Self = Kind + 'Interval'

%{
selfDocComment = """
/// A half-open `IntervalType`, which contains its `start` but not its
/// `end`.  Can represent an empty interval.""" if Kind == 'HalfOpen' else """
/// A closed `IntervalType`, which contains both its `start` and its
/// `end`.  Cannot represent an empty interval."""

selfDocComment += """
///
/// - parameter Bound: The type of the endpoints."""
}%

${selfDocComment}
public struct ${Self}<Bound : Comparable>
  : IntervalType, Equatable, CustomStringConvertible, CustomDebugStringConvertible, _Reflectable {

  @available(*, unavailable, renamed="Bound")
  public typealias T = Bound

  /// Construct a copy of `x`.
  public init(_ x: ${Self}) {
    // This initializer exists only so that we can have a descriptive
    // debugDescription that actually constructs the right type.
    self = x
  }

  /// Construct an interval with the given bounds.
  ///
  /// - Requires: `start <= end`.
  public init(_ start: Bound, _ end: Bound) {
    _precondition(end >= start, "Invalid ${Self} bounds (end < start)")
    _start = start
    _end = end
  }

  /// The `Interval`'s lower bound.
  ///
  /// Invariant: `start` <= `end`.
  public var start: Bound {
    return _start
  }

  /// The `Interval`'s upper bound.
  ///
  /// Invariant: `start` <= `end`.
  public var end: Bound {
    return _end
  }

  /// A textual representation of `self`.
  public var description: String {
    return "\(start)${rangeOperator}\(end)"
  }

  /// A textual representation of `self`, suitable for debugging.
  public var debugDescription: String {
    return "${Self}(\(String(reflecting: start))${rangeOperator}\(String(reflecting: end)))"
  }

  /// Returns `true` iff the `Interval` contains `x`.
  @warn_unused_result
  public func contains(x: Bound) -> Bool {
    return x >= start && x ${upperBoundCompare} end
  }

  /// Returns `intervalToClamp` clamped to `self`.
  ///
  /// The bounds of the result, even if it is empty, are always limited to the bounds of
  /// `self`.
  @warn_unused_result
  public func clamp(intervalToClamp: ${Self}) -> ${Self} {

    return ${Self}(
      self.start > intervalToClamp.start ? self.start
        : self.end < intervalToClamp.start ? self.end
        : intervalToClamp.start,

      self.end < intervalToClamp.end ? self.end
        : self.start > intervalToClamp.end ? self.start
        : intervalToClamp.end
    )
  }


  /// Returns a mirror that reflects `self`.
  public func _getMirror() -> _MirrorType {
    return _IntervalMirror(self)
  }

  internal var _start: Bound
  internal var _end: Bound
}

/// Two `${Self}`s are equal if their `start` and `end` are equal.
@warn_unused_result
public func == <Bound : Comparable> (
  lhs: ${Self}<Bound>, rhs: ${Self}<Bound>
) -> Bool {
  return lhs.start == rhs.start && lhs.end == rhs.end
}
%end

extension HalfOpenInterval {
  /// `true` iff the `Interval` is empty.
  public var isEmpty: Bool {
    return end <= start
  }
}

extension ClosedInterval {
  /// `true` iff the `Interval` is empty.  In the case of
  /// `ClosedInterval`, always returns `false`.
  public var isEmpty: Bool {
    return false
  }
}

extension IntervalType {
  /// Returns `true` if `lhs` and `rhs` have a non-empty intersection.
  @warn_unused_result
  public func overlaps<
    I: IntervalType where I.Bound == Bound
  >(other: I) -> Bool {
    return contains(other.start) || other.contains(start)
  }
}

@available(*, unavailable, message="call the 'overlaps(other)' method on the interval")
public func overlaps<
  I0: IntervalType, I1: IntervalType where I0.Bound == I1.Bound
>(lhs: I0, _ rhs: I1) -> Bool {
  fatalError("unavailable function can't be called")
}

/// Returns a half-open interval from `start` to `end`.
@warn_unused_result
public func ..< <Bound : Comparable>(
  start: Bound, end: Bound
) -> HalfOpenInterval<Bound> {
  return HalfOpenInterval(start, end)
}

/// Returns a closed interval from `start` through `end`.
@warn_unused_result
public func ... <Bound : Comparable>(
  start: Bound, end: Bound
) -> ClosedInterval<Bound> {
  return ClosedInterval(start, end)
}

/// Returns `true` iff `pattern` contains `value`.
@warn_unused_result
public func ~= <I: IntervalType>(pattern: I, value: I.Bound) -> Bool {
  return pattern.contains(value)
}

// Reflection support
%import gyb
%TBoilerplate = gyb.parseTemplate("../common/MirrorBoilerplate.gyb")

%Boilerplate = gyb.executeTemplate(TBoilerplate,\
%                                  introspecteeType='T',\
%                                  disposition='Struct')

internal struct _IntervalMirror<
  T : protocol<IntervalType, CustomStringConvertible>
> : _MirrorType {
  ${Boilerplate}

  internal var count: Int { return 2 }

  internal subscript(i: Int) -> (String, _MirrorType) {
    switch i {
    case 0: return ("start", _reflect(_value.start))
    case 1: return ("end", _reflect(_value.end))
    default: _preconditionFailure("_MirrorType access out of bounds")
    }
  }

  internal var summary: String { return _value.description }

  internal var quickLookObject: PlaygroundQuickLook? {
    return .Some(.Text(summary))
  }
}
